package com.radiant.data.entity;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;

import javax.ws.rs.core.MultivaluedMap;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.commons.beanutils.Converter;

import com.radiant.data.annotation.Entity;
import com.radiant.data.annotation.Persist;

@XmlRootElement
@Entity(name="DevelopmentTrack")
public class DevelopmentTrack {

	@Persist(name="developmentTrackId", itemName=true)
	private String developmentTrackId;

	@Persist(name="child")
	private String childId;

	@Persist(name="month")
	private Month month;

	@Persist(name="should", multiValue=true, multiValueType=TrackItem.class)
	private List<TrackItem> should = new ArrayList<TrackItem>();

	@Persist(name="may", multiValue=true, multiValueType=TrackItem.class)
	private List<TrackItem> may = new ArrayList<TrackItem>();
	
	@Persist(name="probable", multiValue=true, multiValueType=TrackItem.class)
	private List<TrackItem> probable = new ArrayList<TrackItem>();
	
	@Persist(name="eat", multiValue=true, multiValueType=TrackItem.class)
	private List<TrackItem> eat;
	
	@Persist(name="sleep", multiValue=true, multiValueType=TrackItem.class)
	private List<TrackItem> sleep = new ArrayList<TrackItem>();

	@Persist(name="height")
	private Height height;
	
	@Persist(name="weight")
	private Weight weight;	

	public enum MilestoneType {
		SHOULD, MAY, PROBABLE, EAT, SLEEP;
	}

	public static class TrackItem {
		private String id;
		private String value;
		private boolean completed;

		public TrackItem() {
			super();
			id = UUID.randomUUID().toString();
		}
		
		public String getId() {
			return id;
		}

		public void setId(String id) {
			this.id = id;
		}

		public String getValue() {
			return value;
		}

		public void setValue(String value) {
			this.value = value;
		}

		public boolean isCompleted() {
			return completed;
		}

		public void setCompleted(boolean completed) {
			this.completed = completed;
		}

		public String toString() {
			return id + ":" + value + ":" + completed;
		}

		public void initFromString(String msString) {
			StringTokenizer st = new StringTokenizer(msString, ":");
			this.id = st.nextToken();
			this.value = st.nextToken();
			this.completed = Boolean.parseBoolean(st.nextToken());		
		}

		static Converter getConverter() {
			return new Converter() {

				public Object convert(Class type, Object value) {
					if (type == TrackItem.class) {
						TrackItem ms = new TrackItem();
						ms.initFromString((String)value);
						return ms;
					} else if (type == String.class) {
						TrackItem ms = (TrackItem) value;
						return ms.toString();
					} else {
						return value;
					}
				}
				
			};
		}
	}
	
	public static class Weight {
		private float wtInPounds;
		private static final float CONVERSION_RATIO = (float) (1/2.204);
		
		public Weight() {
			
		}

		public Weight(float wt, boolean pounds) {
			if (pounds) {
				this.wtInPounds = wt;
			} else {
				this.wtInPounds = wt/CONVERSION_RATIO;
			}
		}
		
		public float getWtInPounds() {
			return wtInPounds;
		}

		public void setWtInPounds(float wtInPounds) {
			this.wtInPounds = wtInPounds;
		}

		public float inPounds() {
			return wtInPounds;
		}

		public float inKgs() {
			return wtInPounds * CONVERSION_RATIO;
		}

		static Converter getConverter() {
			return new Converter() {

				public Object convert(Class type, Object value) {
					if (type == Weight.class) {
						Weight wt = new Weight(Float.valueOf((String)value), true);
						return wt;
					} else if (type == String.class) {
						Weight wt = (Weight) value;
						return Float.toString(wt.inPounds());
					} else {
						return value;
					}
				}
				
			};
		}

	}

	public static class Height {

		private float htInInches;
		private static final float CONVERSION_RATIO = (float) (2.54);
		
		public Height() {
			
		}

		public Height(float ht, boolean inches) {
			if (inches) {
				this.htInInches = ht;
			} else {
				this.htInInches = ht*CONVERSION_RATIO;
			}
		}

		public float getHtInInches() {
			return htInInches;
		}


		public void setHtInInches(float htInInches) {
			this.htInInches = htInInches;
		}

		public float inInches() {
			return htInInches;
		}

		public float inCms() {
			return htInInches * CONVERSION_RATIO;
		}
		
		static Converter getConverter() {
			return new Converter() {

				public Object convert(Class type, Object value) {
					if (type == Height.class) {
						Height ht = new Height(Float.valueOf((String)value), true);
						return ht;
					} else if (type == String.class) {
						Height ht = (Height) value;
						return Float.toString(ht.inInches());
					} else {
						return value;
					}
				}
				
			};
		}

	}

	public String getChildId() {
		return childId;
	}

	public void setChildId(String childId) {
		this.childId = childId;
	}

	public Month getMonth() {
		return month;
	}

	public void setMonth(Month month) {
		this.month = month;
	}

	public List<TrackItem> getShould() {
		return should;
	}

	public void setShould(List<TrackItem> should) {
		this.should = should;
	}

	public void updateTrackItems(MultivaluedMap<String, String> updatedTrackItems, MilestoneType type) {
		switch (type) {
			case SHOULD: 
				updateExistingValues(updatedTrackItems, should);
				break;
			case MAY: 
				updateExistingValues(updatedTrackItems, may);
				break;
			case PROBABLE: 
				updateExistingValues(updatedTrackItems, probable);
				break;
			case EAT: 
				updateExistingValues(updatedTrackItems, eat);
				break;
			case SLEEP: 
				updateExistingValues(updatedTrackItems, sleep);
				break;
		}
	}

	private void updateExistingValues(MultivaluedMap<String, String> trackMap, List<TrackItem> existingList) {
		for (TrackItem ti: existingList) {
			if(trackMap.containsKey(ti.id)) {
				ti.setValue(trackMap.getFirst(ti.id));
			}
		}
	}
	
	public List<TrackItem> getMay() {
		return may;
	}

	public void setMay(List<TrackItem> may) {
		this.may = may;
	}

	public List<TrackItem> getProbable() {
		return probable;
	}

	public void setProbable(List<TrackItem> probable) {
		this.probable = probable;
	}

	public List<TrackItem> getEat() {
		return eat;
	}

	public void setEat(List<TrackItem> eat) {
		this.eat = eat;
	}

	public List<TrackItem> getSleep() {
		return sleep;
	}

	public void setSleep(List<TrackItem> sleep) {
		this.sleep = sleep;
	}

	public Height getHeight() {
		return height;
	}

	public void setHeight(Height height) {
		this.height = height;
	}

	public Weight getWeight() {
		return weight;
	}

	public void setWeight(Weight weight) {
		this.weight = weight;
	}

	public String getDevelopmentTrackId() {
		return developmentTrackId;
	}

	public void setDevelopmentTrackId(String developmentTrackId) {
		this.developmentTrackId = developmentTrackId;
	}

}
